import React from "react"
import {DataResult, PromiseMabe, VJson} from "./types"
import {IReactionPublic, Lambda} from "mobx/lib/internal"
import {IAutorunOptions, IReactionOptions} from "mobx/lib/api/autorun"
import * as mobx from "mobx"
import {IWhenOptions} from "mobx/lib/api/when"
import * as Extend from "./extend"
import _, { forEach } from "lodash"
import {render as reactDOMRender} from "react-dom"
import {RootRenderer} from "./factory"
import {ConfigProvider, Result, message} from "antd"
import zhCN from "antd/es/locale/zh_CN"
import {Watcher, WhenRunner} from "./watch"
import {DataSourceOption, DataSourceWatcher} from "./data"
import {watchTransform} from "./filter/watchdefine";
import {eventTransform} from "./filter/eventname";
import {rowsCols} from "./filter/rowcols";
import {applyValue, setLoading, setUnLoading} from "./utils/helper";
import {observer, Observer} from 'mobx-react'

/*===================================================================================================
 * 业务模块( YvanScope )
 =================================================================================================== */

export interface BeforeRenderParam {
    vjson: VJson
    cancel: boolean
}

export interface BeforeDestoryParam {
    cancel: boolean
}

Extend.pushVJsonProcess(rowsCols)
Extend.pushVJsonProcess(watchTransform)
Extend.pushVJsonProcess(eventTransform)

/**
 * vjson 处理链
 * @param me scope对象
 * @param vjson 待处理的 VJson
 * @return 返回处理 VJson 的 Promise 异步结果
 */
export function vjsonProcess<INP, Ref>(me: Scope<INP, Ref>, vjson: VJson): Promise<VJson> {

    let doing: Promise<VJson>
    _.forEach(Extend.vjsonProcessChain, (func) => {
        if (!doing) {
            doing = func.call(me, vjson)
        } else {
            doing = doing.then(func.bind(me))
        }
    })

    return doing!
}

export function jsonToRootRender<INP, Ref>(me: Scope<INP, Ref>,
                                    vjsonCallback?: (vjson: VJson) => void): Promise<JSX.Element> {

    return new Promise<JSX.Element>(resolve => {
        let {vjson} = me

        if (typeof vjson === 'function') {
            vjson = vjson.call(me)
        }

        applyValue(vjson).then(vjson => {
            vjsonProcess(me, vjson).then(vjson => {
                if (vjsonCallback) {
                    vjsonCallback(vjson)
                }

                me.vjson = me.$$(vjson)
                // me.vjson = vjson

                const $context = {
                    scopeInstance: me,
                    rootVJson: me.vjson,
                    appContext: Extend as any,
                }

                return resolve((
                    <ConfigProvider locale={zhCN}>
                        <RootRenderer
                            vjson={me.vjson}
                            $default={{}}
                            $context={$context}
                        />
                    </ConfigProvider>
                ))

            }).catch(e => {
                console.error(e)
                return resolve((
                    <Result
                        status="error"
                        title={e.toString()}
                    />
                ))
            })
        })
    })
}

/**
 * 业务模块基类
 */
export abstract class Scope<INP, Ref> {

    constructor(input: INP) {
        if (typeof input === 'object') {
            this.inParamter = this.$$(input)
        } else {
            this.inParamter = input
        }
    }

    /**
     * Scope 被打开时 传入的参数
     */
    readonly inParamter!: INP

    /**
     * 用 ref 定义的组件引用
     */
    readonly refs: Partial<Ref> = {}

    /**
     * 获取 VJSON
     */
    vjson: VJson = {}

    /**
     * 渲染前调用, 可以更改 vjson
     */
    onBeforeRender(param: BeforeRenderParam) {
    }

    /**
     * render 后调用
     */
    onRender() {
    }

    /**
     * 每次调入前台显示时调用
     */
    onShow() {
    }

    /**
     * 请求销毁前调用
     */
    onBeforeDestory(param: BeforeDestoryParam) {
    }

    /**
     * 销毁时调用
     */
    onDestory() {
    }

    /**
     * 为当前 target 创建一个可观察代理
     * scope 关闭之后，会自动动卸载
     */
    observable<T>(target: T): T {
        const result = mobx.observable(target)
        this._addDisposer(result)
        return result
    }

    /**
     * 同 this.observable()
     * scope 关闭之后，会自动动卸载
     */
    $$<T>(target: T): T {
        return this.observable(target)
    }

    // /**
    //  * 根据 get 的取值过程，自动运行方法
    //  * scope 关闭之后，会自动动卸载
    //  */
    // autorun(view: (r: IReactionPublic) => any, opts?: IAutorunOptions) {
    //     const disposer = mobx.autorun(view, opts)
    //     this._addDisposer(disposer)
    // }

    /**
     * 观察取值函数 watchFunc 的计算结果，如果发生变化 会自动运行 thenFunc
     * scope 关闭之后，会自动动卸载
     */
    watch<T, R>(watchFunc: (r: IReactionPublic) => T, thenFunc: (arg: T, r: IReactionPublic) => void, opts?: IReactionOptions) {
        const disposer = mobx.reaction(
            watchFunc,
            thenFunc,
            opts
        )
        this._addDisposer(disposer)
    }

    /**
     * 为当前 vjson 结论，开启一个 watch 定义. 执行 VJSON 之后会自动转换为真正的值
     * scope 关闭之后，会自动动卸载
     */
    $watch<T, R>(watchFunc: () => T, thenFunc?: (args: T) => DataResult<R>, opts?: IReactionOptions): Watcher<T, R> {
        const watcher = new Watcher<T, R>(this, watchFunc, opts)
        // @ts-ignore
        watcher.$then(thenFunc)
        this._addDisposer(watcher)
        return watcher
    }

    /**
     * 观察取值函数 predicate 的计算结果, predicate = true 时会自动运行 effect 函数, 运行完之后立即释放
     * scope 关闭之后，会自动动卸载
     */
    when<T, R>(predicate: () => boolean, effect: Lambda, opts?: IWhenOptions) {
        const disposer = mobx.when(
            predicate,
            effect,
            opts
        )
        this._addDisposer(disposer)
    }

    /**
     * 为当前 vjson 赋值，开启一个 when 定义.
     * scope 关闭之后，会自动动卸载
     */
    $when<R>(defaultValue: R, predicate: () => boolean, effect: () => DataResult<R>, opts?: IWhenOptions): WhenRunner<R> {
        const result = new WhenRunner<R>(this, predicate, effect, opts)
        this._addDisposer(result)
        return result
    }

    /**
     * 给定一个 DataSource 定义，执行转换之后会自动赋值给被定义的项
     */
    $get<T>(func: () => PromiseMabe<T>): any {
        return new DataSourceWatcher<T>(this, func)
    }

    private _disposerList: any[] = []

    /**
     * 为 watch 对象制造释放
     */
    private _addDisposer(disposer: any) {
        this._disposerList.push(disposer)
    }

    /**
     * 执行
     */
    private _disposeExecute() {
        _.forEach(this._disposerList, disposer => {
            if (typeof disposer === 'function') {
                disposer()

            } else if (disposer.isValueDefine) {
                disposer.dispose()
            }
        })
        this._disposerList = []
    }

    /**
     * 以新标签页方式打开
     */
    showPage(key: string) {
        let title = ''
        const setVJson = (json: VJson) => {
            if (!json.view || json.view === 'dialog' || json.view === 'drawer') {
                json.view = 'page'
            }
            title = json.title
        }

        jsonToRootRender(this, setVJson).then(jsr => {
            Extend.showPageOption(title, key, jsr)
            // @ts-ignore
            this['$closeDialog'] = () => {
                Extend.closePageOption(jsr)
            }
        })
    }

    /**
     * 以对话框方式打开
     */
    showDialog(sender?: any) {
        let hide: any = undefined
        if (sender) {
            setLoading(sender)
        } else {
            hide = message.loading({
                content: '加载中...',
                duration: 0,
                style: {
                    marginTop: '20vh',
                }
            })
        }
        // const dom = document.createElement("div");
        // dom.id = _.uniqueId('div_')
        // dom.setAttribute('role', 'dialog')
        // document.body.appendChild(dom)
        // const loadingReactData = mobx.observable({
        //     complete: false
        // });
        // const LoadingReact = <Observer>{
        //     () => (
        //         <AntdModal key={_.uniqueId("_")}
        //                    title={<Spin/>}
        //                    visible={!loadingReactData.complete}
        //                    footer={null}
        //                    destroyOnClose={true}>
        //             <Skeleton/>
        //         </AntdModal>
        //     )
        // }</Observer>
        // reactDOMRender(LoadingReact, dom)


        const setVJson = (json: VJson) => {
            json.view = 'dialog'
        }
        jsonToRootRender(this, setVJson).then(jsr => {
            const id = _.uniqueId('div_')

            const dom = document.createElement("div");
            dom.id = _.uniqueId('div_')
            dom.setAttribute('role', 'dialog')
            document.body.appendChild(dom)
            reactDOMRender(jsr, dom)
            // loadingReactData.complete = true
            if (typeof hide !== 'undefined') {
                hide()
            } else {
                setUnLoading(sender)
            }

        }).finally(() => (
            setUnLoading(sender)
        ))
    }
    /**
     * 以layer弹框方式打开
     */
    showLayer(sender?:any){
        let layerVjson;
        const setVJson = (json: VJson) => {
            layerVjson=json;
            json.view = 'layer';
            json.layerId = _.uniqueId('layer_');
        }
        const hide = message.loading({
            content: '加载中...',
            duration: 0,
            style: {
                marginTop: '20vh',
            }
        })
        jsonToRootRender(this, setVJson).then(jsr => {
            if (!jsr) {
                return
            }
            const id = _.uniqueId('div_')
            const dom = document.createElement("div");
            dom.id = _.uniqueId('div_')
            dom.setAttribute('role', 'layer')
            dom.setAttribute('layer_content_id', layerVjson.layerId);
            let page_view_id,drawerList;
            if(!sender){//
                drawerList=$('body');
                document.body.appendChild(dom)
            }else{
                const view_item=sender.props.$context.rootVJson;
                if(view_item.view=='layer'){//多级弹框
                    if(typeof view_item == 'object'){
                        drawerList=$('div[layer_content_id='+view_item.layerId+']').closest('div[aria-roledescription=page]')[0];
                    }
                }else{
                page_view_id=view_item.id;
                }
                if(page_view_id && view_item.view=='page'){
                    drawerList=document.querySelector('div[view_id='+page_view_id+']');
                    // @ts-ignore
                    drawerList!.appendChild(dom);
                }else if(view_item.view=='layer'){//多级弹框
                    // @ts-ignore
                    drawerList!.appendChild(dom);
                }else{
                    document.body.appendChild(dom)
                }
            }
            let pageWidth=$(drawerList).width();//当前页签的宽度
            let pageHeight=$(drawerList).height();//当前页签的高度
            let layerWidth=layerVjson.width;//layer弹框设置的宽度
            if(!layerWidth){//设置layer弹框的默认宽度
                layerWidth=600
            }else{
                let patt1 = new RegExp(/^(100|[1-9]?\d(\.\d\d?)?)%$/);
                let result = patt1.test(layerWidth);
                if(result){
                    layerWidth=layerWidth.replace("%","");
                    layerWidth= pageWidth*layerWidth/100;
                }
            }
            let layerHeight=layerVjson.height;
            if(!layerHeight){
                layerHeight=pageHeight
            }
            const layer_id=drawerList.lastChild;
            let shade,move;
            if(layerVjson.shade){//是否遮罩 遮罩透明度
                shade=layerVjson.shade
            }else{
                shade=0
            }
            if(layerVjson.move==false){//是否允许拖动
                move=false
            }else{
                move='.layui-layer-title'
            }
            //@ts-ignore
            const zIndex=window.layer.zIndex;
            //@ts-ignore
            window.layer.open({
                shade: shade,
                type: 1,
                maxmin: true,
                area: [layerWidth+'px', layerHeight+'px'], //宽高
                resize:true,
                title:layerVjson.title,
                move:move,
                moveOut:true,
                zIndex: zIndex,//重点1
                success:function(layero){
                    reactDOMRender(jsr, layero.find('.layui-layer-content')[0]);
                    if(!sender){//dom
                        dom.appendChild(layero[0]);
                    }else{
                        layer_id.appendChild(layero[0]);
                        if(layerVjson.css){
                            layero.find('.layui-layer-content').addClass(layerVjson.css)
                        }
                        if(!layerVjson.footer){
                            layero.find('.layui-layer-btn').remove();
                        }else{
                            layero.find('.layui-layer-content').height(layero.find('.layui-layer-content').height()-44);
                            const _footer=layero.find('.layer-wrap-footer')[0];
                            layero[0].insertBefore(_footer,layero.find('.layui-layer-resize')[0]);
                        }
                    }
                    hide();
                    //@ts-ignore
                    window.layer.setTop(layero); //重点2
                },
                resizing: function(layero){//拖拽方法
                    if(layerVjson.footer){
                        layero.find('.layui-layer-content').height(layero.find('.layui-layer-content').height()-49);;
                    }
                },
                restore:function(layero){//还原
                    if(layerVjson.footer){
                        layero.find('.layui-layer-content').height(layero.find('.layui-layer-content').height()-49);
                        layero.find('.layer-wrap-footer').show();
                    }
                },
                full:function(layero){//最大化
                    if(layerVjson.footer){
                        if(sender){
                            layero.find('.layui-layer-content').height(layero.find('.layui-layer-content').height()-49);
                        }
                    }
                },
                min:function(layero){//最小化
                    if(layerVjson.footer){
                        layero.find('.layer-wrap-footer').hide();
                    }
                    const layerArr=$(drawerList).find('div[role=layer]');
                    for(let i=0;i<layerArr.length;i++){
                        if($(layerArr[i]).find('.layui-layer-maxmin').length>0){
                            setTimeout(function(){
                                $(layerArr[i]).find('.layui-layer').css('left',i*181)
                            })
                        }
                        if($(layerArr[i]).attr('id')==layero.parent().attr('id')){
                            setTimeout(function(){
                                $(layerArr[i]).find('.layui-layer').css('left',i*181)
                            })
                        }
                    }
                },
                cancel: function(index, layero){
                    let layerBox=layero.parent();
                    //@ts-ignore
                    window.layer.close(index);
                    layerBox.remove();
                }
            });
        }).finally(() => (
            setUnLoading(sender)
        ))
    }
    /**
     * 以抽屉方式打开
     */
    showDrawer(sender?: any, page?: any) {

        let hide: any = undefined
        if (sender) {
            setLoading(sender)
        } else {
            hide = message.loading({
                content: '加载中...',
                duration: 0,
                style: {
                    marginTop: '20vh',
                }
            });
        }

        // const dom = document.createElement("div");
        // dom.id = _.uniqueId('div_')
        // dom.setAttribute('role', 'dialog')
        // document.body.appendChild(dom)
        // const loadingReactData = mobx.observable({
        //     complete: false
        // });
        // const LoadingReact = <Observer>{
        //     () => (
        //         <AntdModal key={_.uniqueId("_")}
        //                    title={<Spin/>}
        //                    visible={!loadingReactData.complete}
        //                    footer={null}
        //                    destroyOnClose={true}>
        //             <Skeleton/>
        //         </AntdModal>
        //     )
        // }</Observer>
        // reactDOMRender(LoadingReact, dom)

        const setVJson = (json: VJson) => {
            json.view = 'drawer'
            json.drawerId = _.uniqueId('drawer_')
        }


        jsonToRootRender(this, setVJson).then(jsr => {
            const dom = document.createElement("div");
            dom.id = this.vjson.drawerId
            dom.setAttribute('role', 'drawer')
            if(sender){//根据sender向上查找page,找到当前打开标签页的内容，将抽屉内容显示在当前页
                const view_item=sender.props.$context.rootVJson;
                const page_view_id=view_item.id;
                if(page_view_id && view_item.view=='page'){
                    const drawerList = document.getElementById(page_view_id + "_"+"drawer");
                    // @ts-ignore
                    drawerList!.appendChild(dom);
                    drawerList!.style.display = 'block';
                }else{
                    document.body.appendChild(dom)
                }
            }else{
                document.body.appendChild(dom)
            }

            reactDOMRender(jsr, dom)
            // loadingReactData.complete = true
            if (typeof hide !== 'undefined') {
                hide()
            } else {
                setUnLoading(sender)
            }

        }).finally(() => (
            setUnLoading(sender)
        ))
    }
    /**
     * 关闭当前 标签/对话框/抽屉
     */
    close() {
        // @ts-ignore
        const closeDialog: Function = this['$closeDialog']
        if (typeof closeDialog === 'function') {
            closeDialog()
        }
    }
}
